/*  Game/Army Editor for yALB - Yet Another List Builder (roster Editor for Wargames/tabletop games).
    Copyright (C) 2008-2012  Daniel Ricardo Castro Alvarado

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.*/

#include "itemselector.h"
#include "ui_itemselector.h"
#include <QDebug>

// \FIXME: Not working for DualCollection
// \FIXME: Adapt new signals

ItemSelector::ItemSelector(const ItemCollection &_items, const CategoryCollection &_categories,
                           QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ItemSelector)
{
    ui->setupUi(this);

    items = &_items;
    categories = &_categories;

    /*connect(categories, SIGNAL(categoryAdded(GameCategory,QString)),
            this, SLOT(addCategory(GameCategory,QString)));
    connect(categories, SIGNAL(categoryUpdated(QString,GameCategory,QString)),
            this, SLOT(updateCategory(QString,GameCategory,QString)));
    connect(categories, SIGNAL(categoryRemoved(QString)), this,
            SLOT(removeCategory(QString)));

    connect(items, SIGNAL(itemAdded(GameItem,QString)), this,
            SLOT(addItem(GameItem,QString)));
    connect(items, SIGNAL(itemUpdated(QString,GameItem,QString)),
            this, SLOT(updateItem(QString,GameItem,QString)));
    connect(items, SIGNAL(itemRemoved(QString)), this,
            SLOT(removeItem(QString)));

    connect(items, SIGNAL(cleared()), this, SLOT(removeAll()));*/
}

ItemSelector::~ItemSelector()
{
    delete ui;
}

void ItemSelector::clearAll() {
    ui->excludedItemTree->clear();
    ui->includedItemTree->clear();
    ui->appendedString->setText("");
    addedCategories.clear();
    excludedItems.clear();
    addedItems.clear();
    appendedString = "";
}

int ItemSelector::findItemData(const QTreeWidget &treeWidget, const QVariant& data) {
    for(int i = 0; i<treeWidget.topLevelItemCount(); ++i) {
        if(treeWidget.topLevelItem(i)->data(0, Qt::UserRole) == data)
            return i;
    }
    return -1;
}


void ItemSelector::fillFromString(const QString& value) {
    // Fills ItemSelector with the current categories/items

    clearAll();

    QStringList temp = value.split(',');

    // Classify acording the format (>CategoryKey,!excludedItemKey,includedItemKey)

    if(!temp.isEmpty()) {
        for(int i = 0; i<temp.count(); ++i) {
            if(!temp.at(i).isEmpty()) {
                if(temp.at(i).at(0) == '>') {
                    if(categories->contains(temp.at(i).mid(1))) {
                        if(!addedCategories.contains(temp.at(i)))
                            addedCategories.append(temp.at(i));
                    } else
                        appendedString += temp.at(i) + ",";
                } else if(temp.at(i).at(0) == '!') {
                    if(items->contains(temp.at(i).mid(1))) {
                        if(!excludedItems.contains(temp.at(i)))
                            excludedItems.append(temp.at(i));
                    } else
                        appendedString += temp.at(i) + ",";
                } else {
                    if(items->contains(temp.at(i))) {
                        if(!addedItems.contains(temp.at(i)))
                            addedItems.append(temp.at(i));
                    } else
                        appendedString += temp.at(i) + ",";
                }
            }
        }
    }

    // Delete the char ',' in the last position

    if(appendedString.length() > 0)
        appendedString.remove(appendedString.count() -1, 1);
    ui->appendedString->setText(appendedString);

    /*if(sortTree) {
        // Add and arrange acording to the tree
        for(int i = 0; i<sortTree->topLevelItemCount(); ++i) {
            QTreeWidgetItem *categoryItem = sortTree->topLevelItem(i);
            QTreeWidgetItem *categoryExcludedItem = NULL;
            QTreeWidgetItem *categoryIncludedItem = NULL;

            for(int a = 0; a<categoryItem->childCount(); ++a) {
                QString key = categoryItem->child(a)->data(0, Qt::UserRole).toString();
                addItemToTree(key, categories, items, categoryIncludedItem, categoryExcludedItem);
            }
        }
    } else {*/
        QHash<QString, QTreeWidgetItem*> categoryIncludedItems;
        QHash<QString, QTreeWidgetItem*> categoryExcludedItems;
        QTreeWidgetItem *noCategoryIncludedItem = NULL;
        QTreeWidgetItem *noCategoryExcludedItem = NULL;

        for(int i = 0; i<itemOrder.count(); ++i) {
            GameItem item = items->get(itemOrder.at(i));

            if(!item.category.isEmpty() || !categories->contains(item.category)) {
                if(!categoryIncludedItems.contains(item.category)) {
                    categoryIncludedItems[item.category] = NULL;
                    categoryExcludedItems[item.category] = NULL;
                }
                addItemToTree(itemOrder.at(i), &categoryIncludedItems[item.category],
                              &categoryExcludedItems[item.category]);
            } else
                addItemToTree(itemOrder.at(i), &noCategoryIncludedItem, &noCategoryExcludedItem);
        }
    //}
}

QStringList ItemSelector::getListFromSelectedItems() {
    QStringList list;
    list.append(addedCategories);
    list.append(excludedItems);
    list.append(addedItems);
    if(!ui->appendedString->text().isEmpty() && ui->appendedString->text().at(0) == ',')
        ui->appendedString->setText(ui->appendedString->text().mid(1));
    list.append(ui->appendedString->text());

    return list;
}

QString ItemSelector::getStringFromSelectedItems(const QString &currentValue) {
    fillFromString(currentValue);
    int dialogResult = this->exec();
    if(dialogResult == QDialog::Accepted) {
        QString result = getListFromSelectedItems().join(",");
        if(result.endsWith(','))
            result.remove(result.size() -1, 1);
        return result;
    } else {
        return currentValue;
    }
}

void ItemSelector::moveTreeWidgetItem(QTreeWidgetItem *currentItem, QTreeWidget &destination) {
    if(currentItem) {
        QTreeWidget *source = currentItem->treeWidget();
        if(source) {
            if(currentItem->parent()) {
                // It is an item

                int categoryIndex = findItemData(destination, currentItem->parent()->data(0, Qt::UserRole));
                if(categoryIndex == -1) {
                    QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(currentItem->parent()->text(0)));
                    item->setData(0, Qt::UserRole, currentItem->parent()->data(0, Qt::UserRole));
                    destination.addTopLevelItem(item);
                    destination.expandItem(item);
                    categoryIndex = destination.topLevelItemCount() -1;
                }

                if(currentItem->parent()->childCount() == 1) {
                    QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(currentItem->text(0)));
                    item->setData(0, Qt::UserRole, currentItem->data(0, Qt::UserRole));
                    destination.topLevelItem(categoryIndex)->addChild(item);
                    delete currentItem->parent();
                } else {
                    currentItem->parent()->removeChild(currentItem);
                    destination.topLevelItem(categoryIndex)->addChild(currentItem);
                }
            } else {
                // It is a category

                int categoryIndex = findItemData(destination, currentItem->data(0, Qt::UserRole));
                if(categoryIndex == -1) {
                    destination.addTopLevelItem(source->takeTopLevelItem(source->indexOfTopLevelItem(currentItem)));
                    destination.expandItem(currentItem);
                } else {
                    int count = currentItem->childCount();
                    for(int i = 0; i<count; ++i) {
                        QTreeWidgetItem* item = currentItem->child(0);
                        currentItem->removeChild(item);
                        destination.topLevelItem(categoryIndex)->addChild(item);
                    }
                    delete currentItem;
                }
            }
        }
    }
}

void ItemSelector::addItemToTree(const QString& itemKey, QTreeWidgetItem** categoryIncludedItem,
                                 QTreeWidgetItem** categoryExcludedItem) {

    // This method adds an item to includedItemTree or excludedItemTree
    // It will add the category QTreeWidgetItem if needed

    GameItem gameItem = items->get(itemKey);
    QString categoryKey = gameItem.category;
    categoryKey = !categoryKey.isEmpty() && categories->contains(categoryKey) ? categoryKey : "";
    QString categoryName = categoryKey.isEmpty() ? tr("No category") : categories->get(categoryKey).getName();
    bool categoryIsIncluded = false;

    // Category "No Category" (empty key) can not be added
    if(!categoryKey.isEmpty() && addedCategories.contains(">" + categoryKey))
        categoryIsIncluded = true;

    if((!addedItems.contains(itemKey) && !categoryIsIncluded) || excludedItems.contains("!" + itemKey)) {
        // Add to QTreeWidget for excluded items
        if(!*categoryExcludedItem) {
            *categoryExcludedItem = new QTreeWidgetItem(ui->excludedItemTree, QStringList(categoryName));
            (*categoryExcludedItem)->setData(0, Qt::UserRole, QVariant(categoryKey));
            ui->excludedItemTree->expandItem(*categoryExcludedItem);
        }
        QTreeWidgetItem *item = new QTreeWidgetItem(*categoryExcludedItem, QStringList(gameItem.getName()));
        item->setData(0, Qt::UserRole, itemKey);
    } else {
        // Add to QTreeWidget for included items
        if(!*categoryIncludedItem) {
            *categoryIncludedItem = new QTreeWidgetItem(ui->includedItemTree, QStringList(categoryName));
            (*categoryIncludedItem)->setData(0, Qt::UserRole, QVariant(categoryKey));
            ui->includedItemTree->expandItem(*categoryIncludedItem);
            qDebug() << "Creating category item (included) for" << categoryKey;
        }
        QTreeWidgetItem *item = new QTreeWidgetItem(*categoryIncludedItem, QStringList(gameItem.getName()));
        item->setData(0, Qt::UserRole, itemKey);
    }
}

void ItemSelector::on_appendItem_clicked()
{
    moveTreeWidgetItem(ui->excludedItemTree->currentItem(), *(ui->includedItemTree));
}

void ItemSelector::on_subtractItem_clicked()
{
    moveTreeWidgetItem(ui->includedItemTree->currentItem(), *(ui->excludedItemTree));
}

void ItemSelector::on_excludedItemTree_itemSelectionChanged()
{
    if(ui->excludedItemTree->currentItem()) {
        ui->includedItemTree->clearSelection();
        ui->appendItem->setEnabled(true);
        ui->subtractItem->setEnabled(false);
    } else
        ui->appendItem->setEnabled(false);
}

void ItemSelector::on_includedItemTree_itemSelectionChanged()
{
    if(ui->includedItemTree->currentItem()) {
        ui->excludedItemTree->clearSelection();
        ui->appendItem->setEnabled(false);
        ui->subtractItem->setEnabled(true);
    } else
        ui->subtractItem->setEnabled(false);
}

void ItemSelector::addCategory(const GameCategory &category, const QString &key) {
    Q_UNUSED(category)
    categoryOrder.append(key);
}

void ItemSelector::updateCategory(const QString& oldKey, const GameCategory& newCategory,
                     const QString& newKey) {
    Q_UNUSED(newCategory)
    if(oldKey != newKey) {
        int index = categoryOrder.indexOf(oldKey);
        if(index > -1)
            categoryOrder.replace(index, newKey);
    }
}

void ItemSelector::removeCategory(const QString& key) {
    categoryOrder.removeOne(key);
}

void ItemSelector::addItem(const GameItem& item, const QString &key) {
    Q_UNUSED(item)
    itemOrder.append(key);
}

void ItemSelector::updateItem(const QString& oldKey, const GameItem& newItem,
                     const QString& newKey) {
    Q_UNUSED(newItem)
    if(oldKey != newKey) {
        int index = itemOrder.indexOf(oldKey);
        if(index > -1)
            itemOrder.replace(index, newKey);
    }
}

void ItemSelector::removeItem(const QString& key) {
    itemOrder.removeOne(key);
}

void ItemSelector::removeAll() {
    categoryOrder.clear();
    itemOrder.clear();
    clearAll();
}

void ItemSelector::accept()
{
    QHash<QString, int> notIncludedMap;
    addedItems.clear();
    addedCategories.clear();
    excludedItems.clear();

    // Create a map with indexes and keys of each category in NOT included tree
    for(int i = 0; i<ui->excludedItemTree->topLevelItemCount(); ++i)
        notIncludedMap.insert(ui->excludedItemTree->topLevelItem(i)->data(0, Qt::UserRole).toString(), i);

    for(int i = 0; i<ui->includedItemTree->topLevelItemCount(); ++i) {
        QTreeWidgetItem *categoryItem = ui->includedItemTree->topLevelItem(i);
        if(categoryItem->data(0, Qt::UserRole).toString().isEmpty()) {
            // "No Category". We must add every item
            for(int a = 0; a<categoryItem->childCount(); ++a)
                addedItems << categoryItem->child(a)->data(0, Qt::UserRole).toString();
        } else {
            // Determine if we should add the category and exclude items or add items individually
            int indexOfNotIncluded = notIncludedMap.value(categoryItem->data(0, Qt::UserRole).toString(), -1);

            if(indexOfNotIncluded == -1) {
                // The whole category is included, nothing to exclude
                addedCategories << ">" + categoryItem->data(0, Qt::UserRole).toString();
            } else {
                QTreeWidgetItem *notIncludedCat = ui->excludedItemTree->topLevelItem(indexOfNotIncluded);
                int totalItems = categoryItem->childCount() + notIncludedCat->childCount();
                if(categoryItem->childCount() >= totalItems / 2.0) { // Forcing to float with / 2.0
                    // Add category and exclude items
                    addedCategories << ">" + categoryItem->data(0, Qt::UserRole).toString();
                    for(int a = 0; a<notIncludedCat->childCount(); ++a)
                        excludedItems << "!" + notIncludedCat->child(a)->data(0, Qt::UserRole).toString();
                } else {
                    // Add items individually
                    for(int a = 0; a<categoryItem->childCount(); ++a)
                        addedItems << categoryItem->child(a)->data(0, Qt::UserRole).toString();
                }
            }
        }
    }

    QDialog::accept();
}

void ItemSelector::changeEvent(QEvent *event) {
    if(event->type() == QEvent::LanguageChange)
        ui->retranslateUi(this);
    QWidget::changeEvent(event);
}
